#ifndef XG_FILE_H
#define XG_FILE_H
////////////////////////////////////////////////////////
#include "yaml.h"
#include "DateTime.h"
#include "MemQueue.h"

#define LogTrace(level, ...) LogThread::Instance()->trace(level, __VA_ARGS__)

#define TraceException(...) 																			\
{																										\
	Exception e(__VA_ARGS__);																			\
	LogTrace(eERR, "throw exception[%d][%s][%s:%d]", e.getErrorCode(), e.what(), __FILE__, __LINE__);	\
	throw e;																							\
}

#define CATCH_EXCEPTION(CODE)												\
try																			\
{CODE}																		\
catch(const Exception& e)													\
{																			\
	LogTrace(eERR, "catch exception[%d][%s]", e.getErrorCode(), e.what());	\
}																			\
catch(const exception& e)													\
{																			\
	LogTrace(eERR, "catch exception[%s]", e.what());						\
}																			\
catch(...)																	\
{																			\
	LogTrace(eERR, "catch unknown exception");								\
}

typedef enum
{
	eDBG = 0,
	eTIP = 1,
	eINF = 2,
	eIMP = 3,
	eERR = 4
} E_LOGLEVEL;

class IFile : public Object
{
public:
	virtual void close() = 0;
	virtual int read(void* data, int size) = 0;
	virtual int write(const void* data, int size) = 0;

	virtual long long tell() const;
	virtual long long size() const;
	virtual long long seek(long long offset, int mode = SEEK_SET);
	
	template<class DataType> bool readObject(DataType& data)
	{
		if (IsSmallEndianSystem() || sizeof(DataType) == 1)
		{
			return sizeof(DataType) == read(&data, sizeof(DataType));
		}
		else
		{
			u_char msg[sizeof(DataType)];
			u_char* src = (u_char*)(msg);
			u_char* dest = (u_char*)(&data) + sizeof(DataType) - 1;

			CHECK_FALSE_RETURN(sizeof(DataType) == read(msg, sizeof(DataType)));

			for (int i = 0; i < sizeof(DataType); i++)
			{
				*dest-- = *src++;
			}

			return true;
		}
	}
	template<class DataType> bool writeObject(const DataType& data)
	{
		if (IsSmallEndianSystem() || sizeof(DataType) == 1)
		{
			return sizeof(DataType) == write(&data, sizeof(DataType));
		}
		else
		{
			u_char msg[sizeof(DataType)];
			u_char* src = (u_char*)(&data);
			u_char* dest = (u_char*)(msg) + sizeof(DataType) - 1;

			for (int i = 0; i < sizeof(DataType); i++)
			{
				*dest-- = *src++;
			}

			return sizeof(DataType) == write(msg, sizeof(DataType));
		}
	}
};

class File : public IFile
{
	FILE* handle = NULL;

public:
	CONSTRUCTOR_FORBID_COPY(File)

	~File();
	void close();
	bool create(const string& filename);
	bool open(const string& filename, const string& mode = "rb+");

	bool eof() const; 
	void flush() const;
	long long tell() const;
	long long size() const;
	int read(void* data, int size);
	int write(const void* data, int size);
	long long seek(long long offset, int mode = SEEK_SET);

	FILE* getHandle() const
	{
		return handle;
	}
};

class XFile : public IFile
{
	HANDLE handle = INVALID_HANDLE_VALUE;

public:
	CONSTRUCTOR_FORBID_COPY(XFile)

	~XFile();
	void close();
	bool unlock();
	bool lock(bool waited = true);
	bool create(const string& filename);
	bool open(const string& filename, E_OPEN_MODE mode = eWRITE);

	void flush() const;
	long long tell() const;
	long long size() const;
	int read(void* data, int size);
	int write(const void* data, int size);
	long long seek(long long offset, int mode = SEEK_SET);

	HANDLE getHandle() const
	{
		return handle;
	}
};

class MemFile : public IFile
{
	int maxsz = 0;
	int offset = 0;
	int writed = 0;
	char* data = NULL;
	SmartBuffer buffer;

public:
	CONSTRUCTOR_FORBID_COPY(MemFile)

	~MemFile();
	void close();
	bool create(int maxsz);
	bool open(SmartBuffer buffer);
	bool open(void* data, int len);
	bool load(const string& filename);
	bool save(const string& filename) const;

	long long tell() const;
	long long size() const;
	bool truncate(int len);
	int read(void* data, int size);
	int write(const void* data, int size);
	long long seek(long long offset, int mode = SEEK_SET);

	char* getData() const
	{
		return buffer.str();
	}
};

class CacheFile : public Object
{
	class Item
	{
	public:
		time_t ctime;
		time_t utime;
		SmartBuffer content;
	};

	int maxfilesize;
	CacheMap<string, Item> cachemap;

public:
	void clear()
	{
		cachemap.clear();
	}
	int getContent(const string& path, SmartBuffer& content)
	{
		time_t utime;

		return getContent(path, content, utime);
	}
	CacheFile(int maxlen = 100, int maxfilesize = 1024 * 1024)
	{
		this->init(maxlen, maxfilesize);
	}
	void init(int maxlen = 100, int maxfilesize = 1024 * 1024)
	{
		this->maxfilesize = stdx::minval(maxfilesize, XG_MEMFILE_MAXSZ);
		this->cachemap.init(maxlen);
	}

	int getContent(const string& path, SmartBuffer& content, time_t& utime);
};


class TextFile : public Object
{
	FILE* handle = NULL;

public:
	CONSTRUCTOR_FORBID_COPY(TextFile)

	~TextFile();
	void close();
	const TextFile& printf(const char* fmt, ...) const;

	long tell() const
	{
		return ftell(handle);
	}
	void flush() const
	{
		fflush(handle);
	}
	FILE* getHandle() const
	{
		return handle;
	}
	const TextFile& tr() const
	{
		fprintf(handle, "\n");
		return *this;
	}
	const TextFile& puts(const char* str) const
	{
		fprintf(handle, "%s\n", str);
		return *this;
	}
	const TextFile& puts(const string& str) const
	{
		return puts(str.c_str());
	}
	bool open(const string& filename, bool inited = false)
	{
		close();
		handle = fopen(filename.c_str(), inited ? "w+" : "a+");
		return handle ? true : false;
	}

	const TextFile& append(char ch) const
	{
		fputc(ch, handle);
		return *this;
	}
	const TextFile& append(char* str) const
	{
		fprintf(handle, "%s", str);
		return *this;
	}
	const TextFile& append(string& str) const
	{
		return append(str.c_str());
	}
	const TextFile& append(const char* str) const
	{
		fprintf(handle, "%s", str);
		return *this;
	}
	const TextFile& append(const string& str) const
	{
		return append(str.c_str());
	}
	template<class NUMBER_TYPE> const TextFile& append(NUMBER_TYPE val) const
	{
		return append(stdx::str(val));
	}

	const TextFile& operator << (char ch) const
	{
		return append(ch);
	}
	const TextFile& operator << (char* str) const
	{
		return append(str);
	}
	const TextFile& operator << (string& str) const
	{
		return append(str);
	}
	const TextFile& operator << (const char* str) const
	{
		return append(str);
	}
	const TextFile& operator << (const string& str) const
	{
		return append(str);
	}
	template<class NUMBER_TYPE> const TextFile& operator << (NUMBER_TYPE val) const
	{
		return append(stdx::str(val));
	}
};

class LogThread : public Thread
{
	friend class LoggerTimer;

	int flag = 0;	// 0-FILE 1-CONSOLE 2-FILE AND CONSOLE
	int level = 2;	// 0-DEBUG 1-TIPS 2-INFO 3-IMPORTANT 4-ERROR
	int index = -1;
	size_t maxfilesize = 0;
	XFile file;
	MemQueue mq;
	string logpath;
	string filename;
	SmartBuffer buffer;
	mutable SpinMutex mtx;
	function<void(const string&)> func;

	bool start();
	bool checkLogFile();
	string getLogFilePath();
	bool write(const char* msg, int len);
	bool print(const char* msg, int len);

public:
	CONSTRUCTOR_FORBID_COPY(LogThread)

	virtual void run();
	static LogThread* Instance();
	virtual bool init(YAMLoader* file);
	virtual bool printf(const char* fmt, ...);
	virtual bool trace(int level, const string& msg);
	virtual bool trace(int level, const char* fmt, ...);
	virtual void callback(function<void(const string&)> func);
	virtual bool init(const string& path, size_t maxsz = 10 * 1024 * 1024, bool sync = false);

	int size() const
	{
		SpinLocker lk(mtx);

		return mq.size();
	}
	bool empty() const
	{
		SpinLocker lk(mtx);

		return mq.empty();
	}
	void wait() const
	{
		while (true)
		{
			if (empty()) break;

			Sleep(1);
		}
	}
	int getLevel() const
	{
		return level;
	}
	int getLogFlag() const
	{
		return flag;
	}
	void setLevel(int level)
	{
		this->level = level;
	}
	void setLogFlag(int flag)
	{
		this->flag = flag;
	}
	int getMaxFileSize() const
	{
		return maxfilesize;
	}
	const char* getLogPath() const
	{
		return logpath.c_str();
	}
};

class Timer
{
	FILE* file;
	string msg;
	LogThread* log;
	unsigned long long ctime;

public:
	~Timer()
	{
		if (msg.length() > 0)
		{
			long gap = getTimeGap();
			if (log) LogTrace(eDBG, "%s : %ldus", msg.c_str(), gap);
			if (file) fprintf(file, "%s : %ldus\n", msg.c_str(), gap);
		}
	}
	void setFile(FILE* file)
	{
		this->file = file;
	}
	long getTimeGap() const
	{
		return GetTime() - ctime;
	}
	void setHeader(const string& msg)
	{
		this->msg = msg;
	}
	void setLogThread(LogThread* log)
	{
		this->log = log;
	}
	Timer(const char* msg = "time cost", FILE* file = stdout, LogThread* log = NULL)
	{
		if (msg && *msg) this->msg = msg;
		this->ctime = GetTime();
		this->file = file;
		this->log = log;
	}
};

class Context : public Object
{
	friend class HttpRequest;
	friend class HttpResponse;

private:
	string path;
	string traceid;
	string loghead;
	TSMap<string, string> attrmap;

	Context(const Context&) = delete;
	Context& operator = (const Context&) = delete;
	void init(const string& traceid, const string& path);

public:
	Context();
	~Context();

	string getPath() const
	{
		return path;
	}
	string getTraceId() const
	{
		return traceid;
	}

	virtual string getAttr(const string& key) const;
	virtual void setAttr(const string& key, const string& val);

	virtual void trace(int level, const string& msg) const;
	virtual void trace(int level, const char* fmt, ...) const;

	static sp<Context> Create(const string& traceid, const string& path);
};

namespace stdx
{
	int GetFileContent(string& content, const string& path);
	int GetFileContent(SmartBuffer& content, const string& path);
	int FindFile(vector<string>& vec, const string& path, const string& filter);
	int GetFolderContent(vector<string>& vec, const string& path, int flag = eNONE, bool containdots = false);
	int GetFolderContent(function<void(string, int, long, time_t)> func, const string& path, int flag = eNONE, bool containdots = false);
};
////////////////////////////////////////////////////////
#endif